package server

import (
	"golang.org/x/protobuf/proto"
	"sync"
)

type EventPushGroupSessionReg struct {
	exit    chan bool
	evtChan chan *eventPushGroupEvent
	ses     *Session
}

type eventPushGroupEvent struct {
	Name string
	Evt  proto.Message
}

type EventPushGroup struct {
	Name string
	// map[*Session]*EventPushGroupSessionReg
	sessions *sync.Map
	puser    *EventPusher
}

func (epg *EventPushGroup) QueueEvent(eventName string, evt proto.Message) {
	m := &eventPushGroupEvent{
		Name: eventName,
		Evt:  evt,
	}

	epg.sessions.Range(func(k, v interface{}) bool {
		reg := v.(*EventPushGroupSessionReg)
		reg.evtChan <- m
		return true
	})
}

func (epg *EventPushGroup) Destroy() {
	epg.sessions.Range(func(k, v interface{}) bool {
		reg := v.(*EventPushGroupSessionReg)
		close(reg.exit)
		return true
	})
}

func (epg *EventPushGroup) RegSession(ses *Session) (newRegistered bool) {
	reg := new(EventPushGroupSessionReg)
	_, loaded := epg.sessions.LoadOrStore(ses, reg)
	if loaded {
		return
	}
	newRegistered = true
	reg.exit = make(chan bool)
	reg.evtChan = make(chan *eventPushGroupEvent, 10)
	reg.ses = ses

	go func(reg *EventPushGroupSessionReg) {
		for {
			select {
			case <-reg.exit:
				return
			case evt := <-reg.evtChan:
				// TODO: to be optimized!
				regsN, ok := epg.puser.eventPushRegs.Load(evt.Name)
				if !ok {
					continue
				}
				regS, ok := regsN.(*sync.Map).Load(reg.ses)
				if !ok {
					continue
				}
				regS.(*sync.Map).Range(func(k, v interface{}) bool {
					v.(*EventPushReg).QueueEvent(evt.Evt)
					return true
				})
			}
		}
	}(reg)
	return
}
